use std::fmt;

fn main() {
    // rust是静态类型语言（在编译时就必须知道所有变量的类型）

    // 常量 可以在任意作用域声明，包括全局作用域（需要显式的类型声明）
    // 1. const 不可改变的值
    // 2. static 具有"static"生命周期的。可以是可变的变量（须使用static mut 关键字）
    // "string"字面量，可以不经改动就被赋值一个static变量，类型标记为&'static str就包含了所要求的生命周期static，其他的引用类型都必须特地声明

    // 命名规范
    // 类型名（包括结构体、枚举和类型别名）：使用大驼峰式命名法（UpperCamelCase），如 TypeName
    // 变量名和函数名：使用小写字母和下划线的蛇形命名法（snake_case），如 variable_name 和 function_name。
    // 常量名：使用大写字母和下划线的蛇形命名法（SCREAMING_SNAKE_CASE），如 CONSTANT_NAME
    // 生命周期参数名：使用小写字母和撇号（tick），如 'a。

    // 注释 单行注释 // 多行注释/* */
    // 文档注释 单行文档注释 ///  多行文档注释 /** */

    // 模块和包导入
    // 模块导入 导入语句放文件顶部，按字母顺序排序
    // 尽量使用绝对导入路径，在导入路径前加入 create:: 或 self::

    // 错误处理
    // 使用Result类型进行错误处理，避免使用panic！和 unwarp（）
    // 使用？运算符进行错误传递

    // 定义常量
    static LANGUAGE:&'static str="rust";
    const THROW:i32 =12;

    // 常量和不可变变量的区别
    // 不允许对常量使用mut，常量不光默认是不可变的，它总是不能变
    // 常量使用关键字const而不是let，并且必须标注类型
    // 常量可以在任何作用域中，包括全局作用域

    // 1 变量
    let a1 = 5;
    let a2: i32 = 34;
    // let flag: bool = assert_eq!(a1, a2);
    // a1 = 34; 必须是可变变量
    println!("{} days", 10);

    // 定义一个可变变量
    let mut b1 = 5;
    let _a = b1 + 1;
    println!("start value {}", b1);
    b1 = 43;
    println!("end value {value}", value = b1);
    println!("end value {:?}", value = b1);

    // 可变变量和不可变量的区别
    let _immutable_binding = 1;
    let mut mutable_binding = 1;
    println!("Before mutation: {}", mutable_binding);
    // 正确代码
    mutable_binding += 1;
    println!("After mutation: {}", mutable_binding);
    // 错误！
    // _immutable_binding += 1; // 报错 不可变变量值是不能进行相关操作的

    // 固定大小类型
    // let b3:1i32 =23;
    // println!("end value {value}", value = b3);

    // 变量解构
    let (a5, mut b5): (bool, bool) = (true, false);
    println!("{0}:{1}", a5, b5);

    // 注意rust println变量不需要%s，rust自动推断出来的
    // 作用域: 一个变量在程序中能够保持合法的范围
    let zyy:i32 =12;
    {
        let zyc1: i32 =14;
        println!("{}",zyc1);
    }
    // println!("{}",zyc1); // 报错 找不到变量zyc1

    // 变量遮蔽：若后面的变量声明的名称和之前的变量相同，则我们说第一个变量被第二个变量同名变量遮蔽了
    let dmzb:i32 =12;
    {
        let dmzb =14;
        println!("{}",dmzb)
    }
    println!("{}",dmzb);
    let dmzb=12;
    assert_eq!(dmzb, 12);

    // 变量冻结
    let mut _mutable_integer = 7i32;

    {
        // 被不可变的 `_mutable_integer` 遮蔽
        let _mutable_integer = _mutable_integer;

        // 报错！`_mutable_integer` 在本作用域被冻结
        // _mutable_integer = 50; //报错
        // 改正 ^ 注释掉上面这行

        // `_mutable_integer` 离开作用域
    }
    // 正常运行！ `_mutable_integer` 在这个作用域没有冻结
    _mutable_integer = 3;


    // rust原生类型
    // 基本类型 布尔类型,数值,字符类型,单元类型
    // 1. 布尔类型
    let t: bool = false;
    // 2.字符类型存储4个字节
    let c = 'c';
    // 3.数值类型
    let num: i32 = 45;
    let num1: f32 = 45.2;

    // 元类型 即（）,其唯一的值也是（）

    //复合类型 字符串,字符串与字符切片,元组,结构体,枚举
    // 4.字符串类型
    let str = "hello";

    // 字符串创建的三种方式
    // 创建一个空字符串（String::new()）
    let mut s = String::new();

    // 从&str类型转化为String类型（String::from）
    let mut hello = String::from("hello");
    println!("{:?},{:?}", hello, s);

    // to_string
    let mut str1 = str.to_string();

    // string 本质上是三个字段， 一个指针，一个容量大小，一个长度
    // 在rust中 编译时大小确定的数据放在栈上，编译时大小不能确定的放在堆上

    // 5.数组[T;N] 切片 &[T]
    // 数组是一组拥有相同类型T的对象的集合，在内存中是连续存储的，使用[]来创建 它们的大小在编译时会被确定。数组的类型标记为 [T; length]
    // 切片（slice）和数组类似，但其大小在编译时是不确定的,
    //  切片是一个双字对象，第一个字是一个指定数据的指针，第二个字是切片的长度 切片借用了数组的一部分，类型标记为&[T]
    let arr = [0, 1, 2, 3, 4];
    let middle = &arr[1..4];
    let mut ten_zero: [i32; 10] = [1; 10];
    println!("{:?}", arr);
    println!("{:?}", middle);
    println!("{:?}", ten_zero);

    // 切片类型：表示引用集合中一段连续的元素序列，切片是一类引用，没有所有权， 常见类型有三种支持切片的操作（String，数组，Vec类型）
    // s[n1..n2]：获取s中index=n1到index=n2(不包括n2)之间的所有元素；
    // s[n1..]：获取s中index=n1到最后一个元素之间的所有元素；
    // s[..n2]：获取s中第一个元素到index=n2(不包括n2)之间的所有元素；
    // s[..]：获取s中所有元素；
    // 其他表示包含范围的方式，如s[n1..=n2]表示取index=n1到index=n2(包括n2)之间的所有元素。

    // 切片类型是一个胖指针，包含两个字段，
    //     第一个字段是指向源数据中切片起点元素的指针
    //     第二个字段是切片数据中包含的元素数量，即切片的长度


    // 动态数组(生成在堆上)
    let verArray: Vec<i32> = Vec::new();
    let verArray1: Vec<i32> = vec![];
    // 迭代器生成
    let v: Vec<_> = (1..5).collect();
    let verArray2 = vec![1, 2, 34, 5];
    // 形式不安全, verArray2[0]
    assert_eq!(v.get(1), Some(&1));
    assert_eq!(v.get(3), None);
    let v = vec![1, 2, 3];
    // 遍历方式
    // for i in &v { .. } // 获得引用
    // for i in &mut v { .. } // 获得可变引用
    // for i in v { .. } // 获得所有权，注意此时Vec的属主将会被转移！！

    println!("{:?},{:?},{:?}", verArray, verArray1, verArray2);

    // 6.元组
    // 元组是一个包含各种类型值的组合，使用括号（）来构造
    // 注意元组元素过长是无法打印的
    let tmple: (i32, &str) = (50, "hello");
    let (fifty, _) = tmple;
    println!("{:?},{:?}", fifty, tmple.1);

    // 7.指针 裸指针*const T和*mut T
    let x = 5;
    // 转换成裸指针类型
    let raw = &x as *const i32;
    let point_at = unsafe { raw };
    println!("{:?}", point_at);

    // 8.HashMap(哈希表)
    use std::collections::HashMap;

    // 声明
    let mut come_from = HashMap::new();
    // 插入
    come_from.insert("WaySLOG", "HeBei");

    // 查找key
    if !come_from.contains_key("elton") {
        println!(
            "Oh, 我们查到了{}个人，但是可怜的Elton猫还是无家可归",
            come_from.len()
        );
    }

    // 根据key删除元素
    come_from.remove("Mike");
    println!("Mike猫的家乡不是火锅！不是火锅！不是火锅！虽然好吃！");

    // 利用get的返回判断元素是否存在
    let who = ["MoGu", "Marisa"];
    for person in &who {
        match come_from.get(person) {
            Some(location) => println!("{} 来自: {}", person, location),
            None => println!("{} 也无家可归啊.", person),
        }
    }

    // 遍历输出
    println!("那么，所有人呢？");
    for (name, location) in &come_from {
        println!("{}来自: {}", name, location);
    }
    println!("打印日志");

    // 别名(type)
    type InType= i32;
    // 注意 别名的名字需要遵循驼峰命名
    // #[allow(non_camel_case_types)] 禁用编译器警告

    // 类型转换
    // rust不提供原生类型之间的隐式类型转换，可以使用as关键字进行显式转换

    let inter=1;
    let strs=inter as u8;
    println!("size of {0} in bytes",std::mem::size_of_val(&inter));

    // from和into
    // from trait允许一种类型定义“怎么根据另一种类型生成自己”
    let my_str="zhangsna";
    let my_string = String::from(my_str);

    // into 就是把from trait倒过来， 如果你为你的类型实现了 From，那么同时你也就免费获得了 Into。

    // tryfrom和tryInto
    // TryFrom 和 TryInto trait 用于易出错的转换，也正因如此，其返回值是 Result 型

    use std::convert::TryFrom;
    use std::convert::TryInto;
    struct EvenNumber1(i32);
    {
        impl TryFrom<i32> for EvenNumber1 {
            type Error = ();

            fn try_from(value: i32) -> Result<Self, Self::Error> {
                if value % 2 == 0 {
                    Ok(EvenNumber1(value))
                } else {
                    Err(())
                }
            }
        }
        let result=   EvenNumber1::try_from(8);
        result.is_ok();
    }

    // ToString和FromStr
    // 要把任何类型转换成string，只需要实现那个类型的ToString trait, 不需要直接这么做 您应该实现fmt::Display trait，它会自动提供 ToString

    use std::fmt;
    struct Circle1 {
        radius: i32
    }

    impl fmt::Display for Circle1 {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
            write!(f, "Circle of radius {}", self.radius)
        }
    }

    let circle = Circle1 { radius: 6 };
    println!("{}", circle.to_string());

    // 解析字符串
    // 将字符串转成数字，用 parse 函数
    let parsed: i32 = "5".parse().unwrap();
    let turbo_parsed = "10".parse::<i32>().unwrap();
}
